﻿// Inner Kit 库
// XML读取类
// Copyright (c) 2024 by 尔西大帝. All rights reserved.
//
// 这个类用于读取XML，并将其转化为交互者友好的类。
//
// XML配置特殊规则：
// 如果一个节点中，配置Disable="true"。则表示此节点会被忽略。
// 在Foreach中循环到此节点时，将跳过。使用GetCount的时候，此节点不会被计数。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-14

#pragma once

#include <cstdint>
#include <cstdlib>

#include "kstring.h"
#include <DirectXMath.h>
#include <EASTL/string.h>
#include <EASTL/string_view.h>
#include <EASTL/vector.h>
#include <base_struct.h>
#include <functional>
#include <memory>
#include <static_string.h>
#include <tinyxml2.h>

using namespace DirectX;

namespace ikit {
class XmlNode;
typedef XmlNode* LPXmlNode;

typedef void (*XmlNodeCallback)(
    const XmlNode& node, void* classOwner, void* itemOwner);

struct XmlValue {
  std::uint64_t id;  // 一个hash值ID
  char* value;       // 字符串值
  int int_value;     // int值
  float float_value; // 浮点值

  inline std::string GbkValue() const { return ikit::utf8_to_gbk(value); }
  std::wstring WString() const;
  // 是否为空。只有常量EMPTY_VALUE的ID会是空值。
  inline bool empty() const { return id == 0; }
  // 返回是否与字符串相等。如果为空字符，则必然不相等。如果要判断是否为空，使用empty()
  inline bool equals(const char* str) const {
    return value == nullptr ? false : str_equal(value, str);
  }
  operator bool() const { return int_value != 0; }
  operator int() const { return int_value; }
  operator uint32_t() const { return int_value; }
  operator float() const { return float_value; }
  operator std::string() const;
  operator std::string_view() const;
  operator eastl::string() const;
  operator eastl::string_view() const;
  operator sstr() const;
  operator XMFLOAT4() const;
  operator XMFLOAT3() const;
  operator XMVECTOR() const;
  operator XMFLOAT2() const;
  operator XMINT4() const;
  operator XMINT3() const;
  operator XMINT2() const;
  operator Rect() const;
  operator Point() const;
  operator Size() const;
  operator RectF() const;
  operator PointF() const;
  operator SizeF() const;
  operator size_t() const;

  void Release();
};

class XmlNode {
public:
  XmlNode();
  ~XmlNode();

  // 从XML文件中读取
  // TODO：这东西破坏了封装。我得想个办法使tinyxml2不在外面。
  void Read(tinyxml2::XMLElement* xml_node);

  inline bool IsEqual(const char* str) const {
    return str_equal(value_.value, str);
  }
  inline uint64_t ID() const { return value_.id; }
  inline const char* Value() const { return value_.value; }
  inline const char* Text() const {
    return value_.value == nullptr ? EMPTY_TEXT : value_.value;
  }
  inline std::string GbkValue() const {
    return ikit::utf8_to_gbk(value_.value);
  }
  inline std::wstring WString() const { return value_.WString(); }
  inline bool Enable() const { return value_.int_value; }
  inline int Int() const { return value_.int_value; }
  inline unsigned int UInt() const {
    return static_cast<unsigned int>(value_.int_value);
  }

  inline int Int(int defaultValue) const {
    return value_.int_value == 0 ? defaultValue : value_.int_value;
  }
  inline float Float() const { return value_.float_value; }
  inline float Float(float defaultValue) const {
    return value_.float_value == 0.0f ? defaultValue : value_.float_value;
  }
  inline bool HasChild() const { return children_count_ > 0; }
  inline int GetChildCount() const { return children_count_; }
  inline const XmlNode& Get(size_t index) const { return children_[index]; }

  const XmlNode& operator[](const char* key) const;
  const XmlNode& Get(const char* key) const;
  bool IsDisable() const;
  const XmlValue& Attr(const char* key) const;
  // 这个重载运算符返回当前的值。无论这个值是属性还是参数
  const XmlValue& operator()(const char* key) const;
  XMFLOAT4 operator()(const char* key, const XMFLOAT4 default_value) const;
  XMFLOAT3 operator()(const char* key, const XMFLOAT3 default_value) const;
  XMVECTOR operator()(const char* key, const XMVECTOR default_value) const;
  XMFLOAT2 operator()(const char* key, const XMFLOAT2 default_value) const;
  XMINT4 operator()(const char* key, const XMINT4 default_value) const;
  XMINT3 operator()(const char* key, const XMINT3 default_value) const;
  XMINT2 operator()(const char* key, const XMINT2 default_value) const;
  Rect operator()(const char* key, const Rect default_value) const;
  Point operator()(const char* key, const Point default_value) const;
  Size operator()(const char* key, const Size default_value) const;
  RectF operator()(const char* key, const RectF default_value) const;
  PointF operator()(const char* key, const PointF default_value) const;
  SizeF operator()(const char* key, const SizeF default_value) const;
  size_t operator()(const char* key, const size_t default_value) const;
  const bool operator()(const char* key, bool default_value) const;
  const int operator()(const char* key, int default_value) const;
  const float operator()(const char* key, float default_value) const;
  const char* operator()(const char* key, const char* default_value) const;

  void Foreach(
      const char* key, std::function<void(XmlNode&, int)> callback) const;
  // 检查一个Node下面的项目，如果项目是文件，则操作文件，如果是目录，则操作下面的所有文件
  void ForeachPaths(
      const char* key, const std::function<void(const char*)>& callback) const;

  int GetCount(const char* key) const;

  /// <summary>
  /// 将一个列表读入到一个vector，操作的快捷方式
  /// </summary>
  template <typename T>
  void ReadItems(const char* key, eastl::vector<T>& items) const {
    items.reserve(GetCount(key));
    Foreach(key, [&items](XmlNode& n, int) { items.emplace_back(n); });
  }

  /// <summary>
  /// 将一个列表读入到一个vector，设置项目为unique_ptr操作的快捷方式
  /// </summary>
  template <typename T>
  void ReadPtrs(
      const char* key, eastl::vector<std::unique_ptr<T>>& items) const {
    items.reserve(GetCount(key));
    Foreach(key, [&items](XmlNode& n, int) {
      items.emplace_back(std::make_unique<T>(n));
    });
  }
  /// <summary>
  /// 将一个列表读入到一个vector，设置项目为普通指针操作的快捷方式
  /// </summary>
  template <typename T>
  void ReadPtrs(
      const char* key, eastl::vector<T*>& items) const {
    items.reserve(GetCount(key));
    Foreach(key, [&items](XmlNode& n, int) {
      items.emplace_back(new T(n));
    });
  }

  /// <summary>
  /// 将一个列表读入到一个vector，操作的快捷方式。项目的构造函数拥有Index
  /// </summary>
  template <typename T>
  void ReadIndexedItems(const char* key, eastl::vector<T>& items) const {
    items.reserve(GetCount(key));
    Foreach(key, [&items](XmlNode& n, int idx) { items.emplace_back(n, idx); });
  }

private:
  XmlValue value_;       // 自身的值（ID就包括在里面）
  XmlValue* attributes_; // 属性
  int attributes_count_; // 属性的数量
  XmlNode* children_;    // 子项目
  int children_count_;   // 子项目的数量
};

class XmlReader {

public:
  XmlReader() = default;
  XmlReader(const char* filename);
  ~XmlReader() = default;

  void Load(const char* filename);
  const XmlNode& operator[](const char* key) const;
  const XmlNode& Get(const char* key) const;
  const XmlNode& Root() { return root_; }
  const XmlValue& operator()(const char* key) const { return root_(key); }

private:
  XmlNode root_;
};

class XmlInfo {
public:
  // 如果是批量读取的项目，设置在队列中的索引。
  int xml_index;
  // 标记此项目是否有效。如果从一个EmptyNode中读取，此项目设置为false。
  // 如果从一个存在的Node读取，此项目设置为true。
  bool is_validate;
  // 此项目的名称，在大多数情况下通用
  ikit::sstr name;

  XmlInfo(const XmlNode& node, int index)
      : name(node("Name")), is_validate(true), xml_index(index) {
    if (node.ID() == 0) {
      is_validate = false;
    }
  }
};

// 泛型函数，创建并返回一个初始化为 MyData 类型的数组
// 对象必须拥有一个像Object(const XmlNode& node, int index)这样的构造函数。
template <typename T>
eastl::vector<T> Xml_ReadItems(
    const std::string_view& filename, ikit::sstr item_name) {
  // 读取XML文档
  XmlReader xml(filename.data());
  // 得到数量以初始化
  eastl::vector<T> list(xml.Root().GetCount(item_name.c_str()));
  // 遍历每一个项目，以Index设置
  xml.Root().Foreach(
      item_name.c_str(), [&list](const XmlNode& node, int index) {
        list[index] = T(node, index);
      });
  return list;
}

// 同上，但是返回的是指针。用于比较大的对象，或者需要长期保存的对象。
template <typename T>
eastl::vector<std::unique_ptr<T>> Xml_ReadItemsPtrs(
    const std::string_view& filename, ikit::sstr item_name) {
  // 读取XML文档
  XmlReader xml(filename.data());
  // 得到数量以初始化
  eastl::vector<std::unique_ptr<T>> list(
      xml.Root().GetCount(item_name.c_str()));
  // 遍历每一个项目，以Index设置
  xml.Root().Foreach(
      item_name.c_str(), [&list](const XmlNode& node, int index) {
        auto item = std::make_unique<T>(node, index);
        list[index] = std::move(item);
      });
  return list;
}

// 泛型函数，创建并返回一个初始化为 MyData 类型的数组
// 对象必须拥有一个像Object(const XmlNode& node, int index)这样的构造函数。
template <typename T>
eastl::vector<T> XmlNode_ReadItems(const XmlNode& node, ikit::sstr item_name) {
  // 得到数量以初始化
  eastl::vector<T> list(node.GetCount(item_name.c_str()));
  // 遍历每一个项目，以Index设置
  node.Foreach(item_name.c_str(), [&list](const XmlNode& node, int index) {
    list[index] = T(node, index);
  });
  return list;
}

// 同上，但是返回的是指针。用于比较大的对象，或者需要长期保存的对象。
template <typename T>
eastl::vector<std::unique_ptr<T>> XmlNode_ReadItemsPtrs(
    const XmlNode& node, ikit::sstr item_name) {
  // 得到数量以初始化
  eastl::vector<std::unique_ptr<T>> list(node.GetCount(item_name.c_str()));
  // 遍历每一个项目，以Index设置
  node.Foreach(item_name.c_str(), [&list](const XmlNode& node, int index) {
    auto item = std::make_unique<T>(node, index);
    list[index] = std::move(item);
  });
  return list;
}

} // namespace ikit
